package service;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Date;

import server.AnalyticsServer;

public class StatisticsHolder {
	private long beginTime;
	private long minSessionTime = Integer.MAX_VALUE;
	private long maxSessionTime = 0;
	private double avgSessionTime = 0;
	private ArrayList<String> loggedInUsers = new ArrayList<String>();
	private ArrayList<Date> loginDates = new ArrayList<Date>();
	private ArrayList<Long> sessionTimes = new ArrayList<Long>();
	private static AnalyticsServer analyticsServer;
	private ArrayList<Long> auctionTimes = new ArrayList<Long>();
	private int auctionsSuccess = 0;
	private int auctionsTotal = 0;
	private double auctionsSuccessRatio = 0d;
	private ArrayList<Integer> runningAuctions = new ArrayList<Integer>();
	private ArrayList<Date> auctionStartDates = new ArrayList<Date>();
	private double avgAuctionTime = 0;
	private double bidPriceMax = 0;
	private int bidsTotal = 0;
	private static StatisticsHolder instance; 
	private static final Object singletonObject = new Object();

	private StatisticsHolder() {

		beginTime = new Date().getTime();
	}



	public static StatisticsHolder getInstance(AnalyticsServer aServer) {
		if(instance == null) {
			synchronized(singletonObject) {
				if(instance == null) {
					instance = new StatisticsHolder();
					analyticsServer = aServer;
				}
			}
		}
		return instance;
	}

	public void createStatisticsEvent(Event e) throws RemoteException {

		if(e.getType().equals("USER_LOGIN")) {
			String username = ((UserEvent) e).getUserName();
			synchronized(this) {
				if(loggedInUsers.contains(username)) {
					int index = loggedInUsers.indexOf(username);
					long sessionTime = e.getTimestamp().getTime() - loginDates.get(index).getTime();
					if(sessionTime<this.minSessionTime) {
						this.minSessionTime = sessionTime;
						analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_MIN",minSessionTime));
					} else if(sessionTime>this.maxSessionTime) {
						this.maxSessionTime = sessionTime;
						analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_MAX",maxSessionTime));
					}
					sessionTimes.add(sessionTime);
					avgSessionTime = calculateAvgTime(sessionTimes);
					analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_AVG",avgSessionTime));
					loggedInUsers.remove(index);
					loginDates.remove(index);
				}
				
				loggedInUsers.add(username);
				loginDates.add(e.getTimestamp());
			}
			return;
		}
		if(e.getType().equals("USER_LOGOUT") || e.getType().equals("USER_DISCONNECTED")) {
			String username = ((UserEvent) e).getUserName();
			synchronized(this) {
				int index = loggedInUsers.indexOf(username);
				if(index==-1) {
					loggedInUsers.add(((UserEvent) e).getUserName());
					loginDates.add(e.getTimestamp());
					return;
				}
				long sessionTime = e.getTimestamp().getTime() - loginDates.get(index).getTime();
				if(sessionTime<this.minSessionTime) {
					this.minSessionTime = sessionTime;
					analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_MIN",minSessionTime));
				} else if(sessionTime>this.maxSessionTime) {
					this.maxSessionTime = sessionTime;
					analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_MAX",maxSessionTime));
				}
				sessionTimes.add(sessionTime);
				avgSessionTime = calculateAvgTime(sessionTimes);
				analyticsServer.notify(new StatisticsEvent("USER_SESSIONTIME_AVG",avgSessionTime));
				loggedInUsers.remove(index);
				loginDates.remove(index);
			}
			return;
		}
		if(e.getType().equals("AUCTION_STARTED")) {
			synchronized(this) {
				if(runningAuctions.contains(((AuctionEvent) e).getAuctionId())) {
					int auctionId = ((AuctionEvent) e).getAuctionId();
					int index = runningAuctions.indexOf(auctionId);
					
					long auctionTime = e.getTimestamp().getTime() - auctionStartDates.get(index).getTime();

					auctionTimes.add(auctionTime);
					avgAuctionTime = calculateAvgTime(auctionTimes);
					analyticsServer.notify(new StatisticsEvent("AUCTION_TIME_AVG",avgAuctionTime));

					auctionsTotal++;

					runningAuctions.remove(index);
					auctionStartDates.remove(index);	
					
					return;
				}
				runningAuctions.add(((AuctionEvent) e).getAuctionId());
				auctionStartDates.add(e.getTimestamp());
			}
			return;
		}
		if(e.getType().equals("AUCTION_ENDED")) {

			synchronized(this) {
				int auctionId = ((AuctionEvent) e).getAuctionId();
				int index = runningAuctions.indexOf(auctionId);
				if(index == -1) {
					runningAuctions.add(auctionId);
					auctionStartDates.add(e.getTimestamp());
					return;
				}
				//if(index<0) return;
				long auctionTime = e.getTimestamp().getTime() - auctionStartDates.get(index).getTime();

				auctionTimes.add(auctionTime);
				avgAuctionTime = calculateAvgTime(auctionTimes);
				analyticsServer.notify(new StatisticsEvent("AUCTION_TIME_AVG",avgAuctionTime));

				auctionsTotal++;

				runningAuctions.remove(index);
				auctionStartDates.remove(index);	
			}

			return;
		}
		if(e.getType().equals("BID_PLACED")) {
			double bidsPerMinute=0d;
			synchronized(this) {
				if(((BidEvent)e).getPrice()>bidPriceMax) {
					bidPriceMax = ((BidEvent)e).getPrice();
					analyticsServer.notify(new StatisticsEvent("BID_PRICE_MAX",bidPriceMax));
				}
				bidsTotal++;
				bidsPerMinute = (double)((double)bidsTotal/((e.getTimestamp().getTime() - beginTime)/1000/60));
				analyticsServer.notify(new StatisticsEvent("BID_COUNT_PER_MINUTE",bidsPerMinute));
			}
			return;
		}
		if(e.getType().equals("BID_WON")) {
			synchronized(this) {
				auctionsSuccess++;
				if(auctionsSuccessRatio != ((double)auctionsSuccess/auctionsTotal)) {
					auctionsSuccessRatio = ((double)auctionsSuccess/auctionsTotal);
					analyticsServer.notify(new StatisticsEvent("AUCTION_SUCCESS_RATIO",auctionsSuccessRatio));
				}
			}
			return;
		}


		return;
	}

	private double calculateAvgTime(ArrayList<Long> times) {
		long avg = 0;
		double d = 0;
		for(int i=0;i<times.size();i++) {
			avg += times.get(i);
		}
		if(times.size()>0) {
			d = (double)avg / times.size();
		}
		return d;
	}

}